#include <stdlib.h>
#include <stdio.h>
#include "LinkedList.h"

int IsEmpty(const struct LinkedList* const list)
{
  return list ->head == 0;
}

void Add(struct LinkedList* list, void* elem)
{
  int *x = (int *)elem;
  if (IsEmpty(list))
  {
    struct Node* new_node;
    new_node = (struct Node *)malloc(sizeof(struct Node));
    new_node ->element = elem;
    new_node ->next = 0;
    list ->head = new_node;
    //printf("Added %d\n",*(int *)(list ->head ->element));
  }
  else
  {
    struct Node* old_head = list ->head;
    struct Node* new_head = (struct Node *)malloc(sizeof(struct Node));
    new_head ->element = elem;
    new_head ->next = old_head;
    list ->head = new_head;
    //printf("Added %d\n",*((int *)(list ->head ->element)));
  }
}

void PrintList(const struct LinkedList* const list, void (*PrintFunc)(void* element))
{
  if (IsEmpty(list))
  {
    printf("Empty list\n");
  }
  else
  {
    struct Node* current_node = list ->head;
    printf("[");
    while(current_node != 0)
    {
      if (current_node ->next == 0)
        PrintFunc(current_node ->element);
      else
      {
        PrintFunc(current_node ->element);
        printf(",");
      }
      current_node = current_node ->next;
    }
    printf("]\n");
  }
}

int Length(const struct LinkedList* const list)
{
  if (IsEmpty(list))
    return 0;
  else
  {
    int n = 0;
    struct Node* current_node = list ->head;
    while (current_node != 0)
    {
      n++;
      current_node = current_node ->next;
    }
    return n;
  }
}

void Append(struct LinkedList* l1, struct LinkedList* l2)
{
  struct Node* current_node = l1 ->head;
  while (current_node ->next != 0)
    current_node = current_node ->next;
  current_node ->next = l2 ->head;
}

void AddAt(struct LinkedList* list, void* element, const int position)
{
  if(position > Length(list) - 1)
  {
    printf("ERROR at InsertAt: List out of bound\n");
    exit(1);
  }
  else if(IsEmpty(list))
  {
    Add(list,element);
  }
  else
  {
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    struct Node* current_node = list ->head;
    struct Node* new_next;
    int current_pos = 0;
    new_node ->element = element;
    new_node ->next = 0;
    for (current_pos; current_pos < position - 1; current_pos++)
      current_node = current_node ->next;
    new_next = current_node ->next;
    current_node ->next = new_node;
    new_node ->next = new_next;
  }
}

void AddToTail(struct LinkedList* list, void* element)
{
  if (IsEmpty(list))
    Add(list,element);
  else
  {
    struct Node* current_node = list ->head;
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node ->element = element;
    new_node ->next = 0;
    while (current_node ->next != 0)
      current_node = current_node ->next;
    current_node ->next = new_node;
  }
}

void Clear(struct LinkedList* list)
{
  if (!IsEmpty(list))
  {
    struct Node* current_node = list ->head;
    while (current_node != 0)
    {
      struct Node* next = current_node ->next;
      free(current_node);
      current_node = next;
    }
  }
}

struct LinkedList* Copy(const struct LinkedList* const list)
{
  struct LinkedList* new_list = (struct LinkedList*)malloc(sizeof(struct LinkedList));
  if (IsEmpty(list))
  {
    new_list ->head = 0;
    return new_list;
  }
  else
  {
    struct Node* current_node;
    struct Node* current_new_node;
    struct Node* new_head = (struct Node*)malloc(sizeof(struct Node));
    new_head ->element = list ->head ->element;
    new_head ->next = 0;
    new_list ->head = new_head;
    current_node = list ->head ->next;
    current_new_node = new_head;
    while (current_node != 0)
    {
      struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
      new_node ->element = current_node ->element;
      new_node ->next = current_node ->next;
      current_new_node ->next = new_node;
      current_node = current_node ->next;
      current_new_node = new_node;
    }
    return new_list;
  }
}